/*
 * Copyright (c) 2003-2022, KNOPFLERFISH project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following
 * conditions are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials
 *   provided with the distribution.
 *
 * - Neither the name of the KNOPFLERFISH project nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.knopflerfish.bundle.desktop.swing;

import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

/**
 ** A menu that handles Look & Feel changes. for a Java GUI.
 **
 ** This menu contains radio button menu items, one for each look and
 ** feel returned by the method UIManager.getInstalledLookAndFeels.
 ** The name of the menu item is the name of the look and feel and the
 ** action command is the full class name of the JAva class
 ** implementing the look and feel.
 **
 ** @author Gunnar Ekolin, Gatespace AB
 **/
public class LookAndFeelMenu
  extends JMenu
  implements ItemListener
{
  private static final long serialVersionUID = 1L;

  final ButtonGroup lfGroup = new ButtonGroup();

  //  Component root = null;
  LFManager lfManager;

  /**
   * <tt>setRoot</tt> must be called manually when the owning frame is
   * up and visible.
   */
  public LookAndFeelMenu( final String menuName, LFManager lfManager ) {
    super( menuName );

    id++;

    this.lfManager = lfManager;

    final LookAndFeel currentLF = UIManager.getLookAndFeel();
    final String currentLFName  = currentLF!=null ? currentLF.getName() : null;

    final UIManager.LookAndFeelInfo[] iLF
      = UIManager.getInstalledLookAndFeels();

    List<String> classes = new ArrayList<>();
    List<String> names   = new ArrayList<>();

    for(Entry<String,LookAndFeel> entry : lfManager.customLF.entrySet()) {
      final String className = entry.getKey();
      final LookAndFeel lf   = entry.getValue();
      final String name      = lf.getName();

      // Check if custom LF is included in installed LFs
      boolean bIncluded = false;
      for (UIManager.LookAndFeelInfo ilf : iLF) {
        if(ilf.getClassName().equals(className)) {
          bIncluded = true;
          break;
        }
      }

      // If not included, add to my list
      if(!bIncluded) {
        classes.add(className);
        names.add(name);
      }
    }

    // Add all installed LFs
    for (UIManager.LookAndFeelInfo ilf : iLF) {
        classes.add(ilf.getClassName());
        names.add(ilf.getName());
    }

    // Create menu
    for (int i=0;  i<classes.size(); i++) {
      final String className  = classes.get(i);
      final String name = names.get(i);
      JRadioButtonMenuItem rbMenuItem =
        (JRadioButtonMenuItem) add( new JRadioButtonMenuItem( name ) );
      rbMenuItem.setActionCommand( className );

      /*
      rbMenuItem.setAccelerator
        ( KeyStroke.getKeyStroke( KeyEvent.VK_0+ i, ActionEvent.ALT_MASK ) );
      */

      rbMenuItem.getAccessibleContext().setAccessibleDescription
        ("The look and feel option for '"+name+"'.");
      rbMenuItem.setSelected( name.equals( currentLFName ) );
      rbMenuItem.addItemListener( this );

      lfGroup.add( rbMenuItem );
    }

  }//LookAndFeelMenu

  Set<Component> roots = new HashSet<>();

  void addRoot(Component root) {
    roots.add(root);
  }

  static int id = 0;

  public String toString() {
    return "LookAndFeelMenu[id=" + id + "]";
  }

  /** Activate selected L&F */
  public void itemStateChanged(ItemEvent e) {
    /*
    if(root == null) {
      Activator.log.warn("No root component set in LookAndFeelMenu");
    }
    */

    //Ignore all events but selection.
    if (e.getStateChange() != ItemEvent.SELECTED) return;

    // Change to wait cursor

    /*
    if (root!=null) {
      root.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    }
    */

    final JRadioButtonMenuItem rb   = (JRadioButtonMenuItem) e.getSource();
    final String newLFClassName     = rb.getActionCommand();
    final String newLFName          = rb.getText();
    final String currentLFClassName = UIManager.getLookAndFeel().getName();


    LookAndFeel cLF = lfManager.customLF.get(newLFClassName);

    if(cLF != null) {     // First, check if it any of the custom LFs
      try {


        Activator.log.debug("set custom LF classloader to" + cLF.getClass().getClassLoader());
        UIManager.getLookAndFeelDefaults().put("ClassLoader", cLF.getClass().getClassLoader());

        Activator.log.debug("set custom LF " + newLFClassName);
        UIManager.setLookAndFeel(cLF);

        for (Component root : roots) {
          SwingUtilities.updateComponentTreeUI(root);
        }

      } catch (Exception ex) {
        Activator.log.error("Failed to set LF " + newLFClassName, ex);
      }
    } else {  // Otherwise, go for one of the installed LFs
      if (newLFClassName!=null && !newLFClassName.equals(currentLFClassName)) {
        try {
          Activator.log.debug("set installed LF " + newLFClassName);
          UIManager.setLookAndFeel(newLFClassName);
          try {
            for (Component root : roots) {
              SwingUtilities.updateComponentTreeUI(root);
            }

          } catch (NullPointerException npe) {
            Activator.log.error
              ( "Unexpected error while applying new look and feel", npe);
          }
        } catch (UnsupportedLookAndFeelException exc) {
          rb.setEnabled( false );
          Activator.log.error( "Unsupported LookAndFeel: " + newLFName
                               +" ("+newLFClassName+")", exc );

          // Fallback: Set L&F to cross platform L&F
          Activator.log.error( "Reverting to the cross platform LookAndFeel." );
          final String cpLFcn = UIManager.getCrossPlatformLookAndFeelClassName();
          // Find the menu item with the cross platform L&F and select it
          for (Enumeration<AbstractButton> lfBEnum = lfGroup.getElements();
               lfBEnum.hasMoreElements(); ) {
            final AbstractButton ab = lfBEnum.nextElement();
            if (cpLFcn.equals( ab.getActionCommand() )) {
              SwingUtilities.invokeLater(() -> ab.setSelected(true));
              break;
            }
          }
        } catch (Exception exc) {
          rb.setEnabled(false);
          Activator.log.error("Could not load LookAndFeel: " +rb.getText(), exc);
        }
      } else {
        // Selected current LF, noop
      }
    }
    /*
    if(root != null) {
      root.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    */
  }

}// class LookAndFeelMenu
